use super::{current_worker, Attr, FnOnceFuture, JoinHandle, Runtime, Sleep, TaskRef, Yield};
use crate::Error;
use core::future::Future;
use core::time::Duration;

/// 方便在同步环境中调用异步函数的主入口,
/// 异步函数执行结束后本函数才返回，即当前线程阻塞执行，因此对异步函数体无任何要求, 这是和spawn接口的最大区别
pub fn block_on_with<T>(future: T, attr: &Attr) -> Result<T::Output, Error> 
where
    T: Future
{
    if let Ok(task) = sched(future, attr) {
        JoinHandle::<T::Output>::new(task).join()
    } else {
        Err(Error::default())
    }
}

/// 参见block_on_with, 参数attr对应为Attr::default()
pub fn block_on<T>(future: T) -> Result<T::Output, Error>
where
    T: Future
{
    block_on_with(future, &Attr::default())
}

/// 启动一个全新的异步执行任务. 本函数可以在异步环境中调用，也可以在同步环境中调用.
/// 和当前线程并发且结束时间不确定，因此有Send和'static要求.
/// 如果同时创建多个异步任务，建议使用JoinSet, 可以更高效的等待全部任务或者某个任务结束.
pub fn spawn_with<T>(future: T, attr: &Attr) -> JoinHandle<T::Output>
where
    T: Future + Send + 'static,
    T::Output: Send + 'static,
{
    if let Ok(task) = sched(future, attr) {
        JoinHandle::<T::Output>::new(task)
    } else {
        JoinHandle::<T::Output>::null()
    }
}

/// 参见spawn_with, attr为Attr::default()
pub fn spawn<T>(future: T) -> JoinHandle<T::Output>
where
    T: Future + Send + 'static,
    T::Output: Send + 'static,
{
    spawn_with(future, &Attr::default())
}

/// 参见spawn_fn_with, attr为Attr::default()
pub fn spawn_fn<F, R>(f: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    spawn(FnOnceFuture::new(f))
}

/// 将已有的同步函数包装为异步任务执行体.
/// 注意此函数中不要调用阻塞函数，如果必须执行阻塞函数，可以利用attr交给另一个运行时实例来完成.
/// 比如: spawn_fn_with(block_function, &Attr::new(BLOCK_RUNTIME_ID))
pub fn spawn_fn_with<F, R>(f: F, attr: &Attr) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    spawn_with(FnOnceFuture::new(f), attr)
}

/// 启动一个全新的异步执行任务，本函数只能在异步环境中调用，否则JoinHandle::join返回Err.
/// 因为不存在和当前线程的并发，只是任务结束时间不定，因此只有'static生命周期要求，无Send要求.
pub fn spawn_local_with<T>(future: T, attr: &Attr) -> JoinHandle<T::Output>
where
    T: Future + 'static,
    T::Output: 'static,
{
    if let Some(worker) = current_worker() {
        if let Ok(task) = worker.spawn_local(future, attr) {
            return JoinHandle::<T::Output>::new(task);
        }
    }
    JoinHandle::<T::Output>::null()
}

/// 参见spawn_local_with, attr为Attr::default()
pub fn spawn_local<T>(future: T) -> JoinHandle<T::Output>
where
    T: Future + 'static,
    T::Output: 'static,
{
    spawn_local_with(future, &Attr::default())
}

/// 将已有的同步函数包装为异步任务执行体,在当前调度线程执行.
/// 注意函数中不应该有阻塞调用，否则会阻塞当前线程其他任务的执行.
/// 参见spawn_local_with
pub fn spawn_fn_local_with<F, R>(f: F, attr: &Attr) -> JoinHandle<R>
where
    F: FnOnce() -> R + 'static,
    R: 'static,
{
    spawn_local_with(FnOnceFuture::new(f), attr)
}

/// 参见spawn_fn_local, attr为Attr::default()
pub fn spawn_fn_local<F, R>(f: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + 'static,
    R: 'static,
{
    spawn_local(FnOnceFuture::new(f))
}

/// sleep的异步任务可以取消`JoinHandle::abort`，但资源也只有在sleep返回后才释放.
pub async fn sleep(timeout: Duration) {
    Sleep::new(timeout).await
}

/// 放弃当前调度
pub async fn yield_now() {
    Yield::new().await
}

fn sched<T: Future>(future: T, attr: &Attr) -> Result<TaskRef, Error> {
    if let Some(worker) = current_worker() {
        if worker.group_id() == attr.group_id {
            return worker.spawn(future, attr);
        }
    }
    Runtime::get(attr.group_id).spawn(future, attr)
}

#[cfg(test)]
mod test {
    use crate::runtime::*;
    #[test]
    fn test_future() {
        let _ = Builder::new().nth(1).build();
        async fn test_foo() -> i32 {
            100
        }
        let val = spawn(test_foo()).join().unwrap();
        assert_eq!(val, 100);
    }

    #[test]
    fn test_sleep() {
        let _ = Builder::new().nth(1).build();
        async fn test_sleep(val: i32) -> i32 {
            //sleep(Duration::new(1, 0)).await;
            yield_now().await;
            val + 100
        }
        let val = spawn(test_sleep(100)).join().unwrap();
        assert_eq!(val, 200);
    }
}
